#include <GL/glut.h>
#include <stdio.h>
#include <stdlib.h>

#include <vector>
#include <cmath>

#include "glCamera.h"
#include "math_funcs.h"


typedef int BOOL;
#define TRUE 1
#define FALSE 0

glCamera Camera;


void drawRectangle() {
	const GLfloat squareVertices[] = {
		-1.0, 1.0, 0.0,               // Top left
		-1.0, -1.0, 0.0,              // Bottom left
		1.0, -1.0, 0.0,               // Bottom right
		1.0, 1.0, 0.0                 // Top right
	};

	glColor3f(0.8f, 0.0f, 0.0f);

	// translate rectangle wrt. left-right movement of camera

	glVector direction = Camera.GetDirection();


	/*
	float radius = 8.0f;

	// SPHERICAL COORDINATE SYSTEM
	float r = sqrt(direction.i * direction.i + direction.j * direction.j + direction.k * direction.k);
	float sigma = atan(direction.k / direction.i);
	float theta = acos(direction.j / r);

	float d_sigma = RAD2DEG(sigma);
	float d_theta = RAD2DEG(theta);


	glTranslatef(direction.i * radius, direction.j * radius, direction.k * radius * -1.0f);

	if (direction.k < 0.0f) {
		glRotatef(RAD2DEG(theta)-90, 1.0, 0.0, 0.0);
	}
	else if (direction.k >= 0.0f) {
		glRotatef(90-RAD2DEG(theta), 1.0, 0.0, 0.0);	
	}


	glRotatef(RAD2DEG(sigma)-90, 0.0, 1.0, 0.0);



	printf("i: %f j: %f k: %f sigma: %f theta: %f\n ",direction.i, direction.j, direction.k, d_sigma, d_theta);
*/



	GLfloat Matrix[16];

	glQuaternion m_qYaw;
	glQuaternion m_qPitch;
	glQuaternion q;

	// create the quaternions that will represent our rotations
	m_qYaw.CreateFromAxisAngle(0.0f, 1.0f, 0.0f, Camera.m_YawDegrees);
	m_qPitch.CreateFromAxisAngle(1.0f, 0.0f, 0.0f, Camera.m_PitchDegrees);

    // Combine the pitch and Yaw rotations and store the results in q
    q = m_qPitch * m_qYaw;

	q.CreateMatrix(Matrix);

	glVector axis;
	float angle;
	q.GetAxisAngle(axis, angle);
//	q.Print();
//	axis.Print();
//	printf("angle is: %f\n", angle);
	angle = RAD2DEG(angle);


	float radius = 8.0f;
	glTranslatef(direction.i * radius, direction.j * radius, direction.k * radius * -1.0f);
	glRotatef(angle*-1.0f, axis.i, axis.j, axis.k);
//	glMultMatrixf(Matrix);

/*
	// SPHERICAL COORDINATE SYSTEM
	float r = sqrt(direction.i * direction.i + direction.j * direction.j + direction.k * direction.k);
	float sigma = atan(direction.k / direction.i);
	float theta = acos(direction.j / r);
	float d_sigma = RAD2DEG(sigma);
	float d_theta = RAD2DEG(theta);	
	printf("i: %f j: %f k: %f sigma: %f theta: %f\n ",direction.i, direction.j, direction.k, d_sigma, d_theta);
*/
	glVertexPointer(3, GL_FLOAT, 0, squareVertices);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}

void drawSphere() {
	glColor3f(1.0f, 1.0f, 1.0f);
	// rotate sphere draw the wireframe rotated
	glRotatef(90,1.0f ,0.0f, 0.0f);
	glPolygonMode(GL_BACK, GL_LINE); // draw wireframe only on inside of the sphere
	glutSolidSphere(10,70,70);
	glPolygonMode(GL_BACK, GL_FILL);
}

void changeSize(int w, int h) {

	// Prevent a divide by zero, when window is too short
	// (you cant make a window of zero width).
	if (h == 0)
		h = 1;
	float ratio =  w * 1.0 / h;

	// Use the Projection Matrix
	glMatrixMode(GL_PROJECTION);

	// Reset Matrix
	glLoadIdentity();

	// Set the viewport to be the entire window
	glViewport(0, 0, w, h);

	// Set the correct perspective.
	gluPerspective(45.0f, ratio, 0.1f, 100.0f);

	// Get Back to the Modelview
	glMatrixMode(GL_MODELVIEW);
}

void renderScene(void) {

	// Clear Color and Depth Buffers

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW);
	// Reset transformations
	glLoadIdentity();

	Camera.SetPerspective();

	glPushMatrix();
		drawSphere();
	glPopMatrix();

	glPushMatrix();
		drawRectangle();
	glPopMatrix();

	glutSwapBuffers();
}

void processNormalKeys(unsigned char key, int x, int y) {
	switch (key) {
		case 27:
			exit(0);
		case 'a':
			Camera.ChangeRoll(-1.0f);
			break;
		case 'd':
			Camera.ChangeRoll(1.0f);
			break;
	}
}

void processSpecialKeys(int key, int xx, int yy) {
	switch (key) {
		case GLUT_KEY_LEFT :
			Camera.ChangeYaw(-1.0f);
			break;
		case GLUT_KEY_RIGHT :
			Camera.ChangeYaw(1.0f);
			break;
		case GLUT_KEY_UP :
//			if (Camera.m_PitchDegrees < 60.0f) Camera.ChangePitch(1.0f);
			Camera.ChangePitch(1.0f);
			break;
		case GLUT_KEY_DOWN :
//			if (Camera.m_PitchDegrees > -60.0f) Camera.ChangePitch(-1.0f);
			Camera.ChangePitch(-1.0f);
			break;
	}
}

/*  call when mouse button is pressed  */
void MouseClicks(int button, int state, int x, int y) {
}

void MouseMotion(int x, int y)
{
}

int glInit() {
    // Now set up our max values for the camera
	glEnable(GL_DEPTH_TEST);

    Camera.m_MaxForwardVelocity = 5.0f;
    Camera.m_MaxPitchRate = 5.0f;
    Camera.m_MaxRollRate = 5.0f;
    Camera.m_MaxYawRate = 5.0f;
    Camera.m_RollDegrees = 0.0f;
    Camera.m_PitchDegrees = 0.0f;
    Camera.m_YawDegrees = 0.0f;
    // Initialization Went OK
    return TRUE;
}


int main(int argc, char **argv) {

	// init GLUT and create window

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(100,100);
	glutInitWindowSize(480,640);
	glutCreateWindow("Spherical User Interface");

	// register callbacks
	glutDisplayFunc(renderScene);
	glutReshapeFunc(changeSize);
	glutIdleFunc(renderScene);
	glutKeyboardFunc(processNormalKeys);
	glutSpecialFunc(processSpecialKeys);
    glutMouseFunc(MouseClicks);
	glutMotionFunc (MouseMotion);
	// OpenGL init
	glInit();

	// enter GLUT event processing cycle
	glutMainLoop();
}
